AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
-PKG_CHECK_MODULES(MUTTER_PLUGIN, gtk+-2.0 dbus-glib-1 mutter-plugins gjs-gi-1.0 xscrnsaver libgnome-menu $recorder_modules gconf-2.0 gdk-x11-2.0 clutter-x11-0.9 clutter-glx-0.9)
+PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-unix-2.0 gtk+-2.0 dbus-glib-1 mutter-plugins gjs-gi-1.0 xscrnsaver libgnome-menu $recorder_modules gconf-2.0 gdk-x11-2.0 clutter-x11-0.9 clutter-glx-0.9)
PKG_CHECK_MODULES(TIDY, clutter-0.9)
PKG_CHECK_MODULES(BIG, clutter-0.9 gtk+-2.0 librsvg-2.0)
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
jsmiscdir = $(pkgdatadir)/js/misc
dist_jsmisc_DATA = \
- appInfo.js \
docInfo.js
+++ /dev/null
-/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
-
-const Clutter = imports.gi.Clutter;
-const Gio = imports.gi.Gio;
-const Gtk = imports.gi.Gtk;
-const Shell = imports.gi.Shell;
-
-const Main = imports.ui.main;
-
-function AppInfo(appId) {
- this._init(appId);
-}
-
-AppInfo.prototype = {
- _init : function(appId) {
- this.appId = appId;
- this._gAppInfo = Gio.DesktopAppInfo.new(appId);
- if (!this._gAppInfo) {
- throw new Error('Unknown appId ' + appId);
- }
-
- this.id = this._gAppInfo.get_id();
- this.name = this._gAppInfo.get_name();
- this.description = this._gAppInfo.get_description();
- this.executable = this._gAppInfo.get_executable();
-
- this._gicon = this._gAppInfo.get_icon();
- },
-
- createIcon : function(size) {
- if (this._gicon)
- return Shell.TextureCache.get_default().load_gicon(this._gicon, size);
- else
- return new Clutter.Texture({ width: size, height: size });
- },
-
- getIconPath : function(size) {
- if (this._gicon) {
- let iconTheme = Gtk.IconTheme.get_default();
- let previewIconInfo = iconTheme.lookup_by_gicon(this._gicon, size, 0);
- if (previewIconInfo)
- return previewIconInfo.get_filename();
- }
- return null;
- },
-
- launch : function() {
- this._gAppInfo.launch([], Main.createAppLaunchContext());
- }
-};
-
-var _infos = {};
-
-// getAppInfo:
-// @appId: an appId
-//
-// Gets an #AppInfo for @appId. This is preferable to calling
-// new AppInfo() directly, because it caches #AppInfos.
-//
-// Return value: the new or cached #AppInfo, or %null if @appId
-// doesn't point to a valid .desktop file
-function getAppInfo(appId) {
- let info = _infos[appId];
- if (info === undefined) {
- try {
- info = _infos[appId] = new AppInfo(appId);
- } catch (e) {
- info = _infos[appId] = null;
- }
- }
- return info;
-}
-
-// getTopApps:
-// @count: maximum number of apps to retrieve
-//
-// Gets a list of #AppInfos for the @count most-frequently-used
-// applications, with explicitly-chosen favorites first.
-//
-// Return value: the list of #AppInfo
-function getTopApps(count) {
- let appMonitor = Shell.AppMonitor.get_default();
-
- let matches = [], alreadyAdded = {};
-
- let favs = getFavorites();
- for (let i = 0; i < favs.length && favs.length <= count; i++) {
- let appId = favs[i].appId;
-
- if (alreadyAdded[appId])
- continue;
- alreadyAdded[appId] = true;
-
- matches.push(favs[i]);
- }
-
- // Ask for more apps than we need, since the list of recently used
- // apps might contain an app we don't have a desktop file for
- let apps = appMonitor.get_most_used_apps (0, Math.round(count * 1.5));
- for (let i = 0; i < apps.length && matches.length <= count; i++) {
- let appId = apps[i] + ".desktop";
- if (alreadyAdded[appId])
- continue;
- alreadyAdded[appId] = true;
- let appInfo = getAppInfo(appId);
- if (appInfo) {
- matches.push(appInfo);
- }
- }
-
- return matches;
-}
-
-function _idListToInfos(ids) {
- let infos = [];
- for (let i = 0; i < ids.length; i++) {
- let display = getAppInfo(ids[i]);
- if (display == null)
- continue;
- infos.push(display);
- }
- return infos;
-}
-
-function getFavorites() {
- let system = Shell.AppSystem.get_default();
-
- return _idListToInfos(system.get_favorites());
-}
-
-function getRunning() {
- let monitor = Shell.AppMonitor.get_default();
- return _idListToInfos(monitor.get_running_app_ids());
-}
const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const Pango = imports.gi.Pango;
+const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Tidy = imports.gi.Tidy;
const Signals = imports.signals;
const Mainloop = imports.mainloop;
-const AppInfo = imports.misc.appInfo;
const DND = imports.ui.dnd;
const GenericDisplay = imports.ui.genericDisplay;
const Workspaces = imports.ui.workspaces;
__proto__: GenericDisplay.GenericDisplayItem.prototype,
_init : function(appInfo, availableWidth) {
- GenericDisplay.GenericDisplayItem.prototype._init.call(this, availableWidth);
+ GenericDisplay.GenericDisplayItem.prototype._init.call(this, availableWidth);
this._appInfo = appInfo;
- this._setItemInfo(appInfo.name, appInfo.description);
+ this._setItemInfo(appInfo.get_name(), appInfo.get_description());
},
getId: function() {
- return this._appInfo.appId;
+ return this._appInfo.get_id();
},
//// Public method overrides ////
// Returns an icon for the item.
_createIcon : function() {
- return this._appInfo.createIcon(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
+ return this._appInfo.create_icon_texture(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
},
// Ensures the preview icon is created.
_ensurePreviewIconCreated : function() {
if (!this._showPreview || this._previewIcon)
- return;
+ return;
+
+ let gicon = this._appInfo.get_icon();
+ let previewIconPath = null;
+ if (gicon) {
+ let iconTheme = Gtk.IconTheme.get_default();
+ let previewIconInfo = iconTheme.lookup_by_gicon(gicon, GenericDisplay.PREVIEW_ICON_SIZE, 0);
+ if (previewIconInfo)
+ previewIconPath = previewIconInfo.get_filename();
+ }
- let previewIconPath = this._appInfo.getIconPath(GenericDisplay.PREVIEW_ICON_SIZE);
if (previewIconPath) {
try {
- this._previewIcon = new Clutter.Texture({ width: GenericDisplay.PREVIEW_ICON_SIZE, height: GenericDisplay.PREVIEW_ICON_SIZE});
+ this._previewIcon = new Clutter.Texture({ width: GenericDisplay.PREVIEW_ICON_SIZE, height: GenericDisplay.PREVIEW_ICON_SIZE});
this._previewIcon.set_from_file(previewIconPath);
} catch (e) {
// we can get an error here if the file path doesn't exist on the system
this._redisplay(false);
}));
- // Load the GAppInfos now so it doesn't slow down the first
+ // Load the apps now so it doesn't slow down the first
// transition into the overlay
this._refreshCache();
// Protected overrides
_filterActive: function() {
- return !!this._search || this._activeMenuIndex >= 0;
+ // We always have a filter now since a menu must be selected
+ return true;
},
_filterReset: function() {
this._menuDisplays[index].setState(MENU_SELECTED);
},
+ _getMostUsed: function() {
+ return this._appMonitor.get_most_used_apps(0, 30).map(Lang.bind(this, function (id) {
+ return this._appSystem.lookup_app(id + '.desktop');
+ })).filter(function (e) { return e != null });
+ },
+
_addMenuItem: function(name, id, icon, index) {
let display = new MenuItem(name, id, icon);
this._menuDisplays.push(display);
this._activeMenuIndex = index;
this._activeMenu = display;
if (id == null) {
- this._activeMenuApps = this._appMonitor.get_most_used_apps(0, 30);
+ this._activeMenuApps = this._getMostUsed();
} else {
this._activeMenuApps = this._appSystem.get_applications_for_menu(id);
}
}
},
- _addAppForId: function(appId) {
- let appInfo = AppInfo.getAppInfo(appId);
- if (appInfo != null) {
- this._addApp(appInfo);
- } else {
- log("appInfo for " + appId + " was not found.");
- }
- },
-
_addApp: function(appInfo) {
- let appId = appInfo.id;
+ let appId = appInfo.get_id();
this._allItems[appId] = appInfo;
- // [] is returned if we could not get the categories or the list of categories was empty
- let categories = Shell.get_categories_for_desktop_file(appId);
- this._appCategories[appId] = categories;
},
- //// Protected method overrides ////
+ //// Protected method overrides ////
// Gets information about all applications by calling Gio.app_info_get_all().
_refreshCache : function() {
this._appCategories = {};
this._menus = this._appSystem.get_menus();
-
// Loop over the toplevel menu items, load the set of desktop file ids
// associated with each one
for (let i = 0; i < this._menus.length; i++) {
let menu = this._menus[i];
let menuApps = this._appSystem.get_applications_for_menu(menu.id);
for (let j = 0; j < menuApps.length; j++) {
- let appId = menuApps[j];
- this._addAppForId(appId);
+ let app = menuApps[j];
+ this._addApp(app);
}
}
// These show up in search, but not with the rest of apps.
let settings = this._appSystem.get_all_settings();
for (let i = 0; i < settings.length; i++) {
- let appId = settings[i];
- this._addAppForId(appId);
+ let app = settings[i];
+ this._addApp(app);
}
this._appsStale = false;
},
- // Sets the list of the displayed items based on the most used apps.
+ // Stub this out; the app display always has a category selected
_setDefaultList : function() {
- let matchedInfos = AppInfo.getTopApps(MAX_ITEMS);
- this._matchedItems = matchedInfos.map(function(info) { return info.appId; });
+ this._matchedItems = [];
},
// Compares items associated with the item ids based on the alphabetical order
_compareItems : function(itemIdA, itemIdB) {
let appA = this._allItems[itemIdA];
let appB = this._allItems[itemIdB];
- return appA.name.localeCompare(appB.name);
+ return appA.get_name().localeCompare(appB.get_name());
},
// Checks if the item info can be a match for the search string by checking
- // the name, description, execution command, and categories for the application.
- // Item info is expected to be GAppInfo.
+ // the name, description, execution command, and categories for the application.
+ // Item info is expected to be Shell.AppInfo.
// Returns a boolean flag indicating if itemInfo is a match.
_isInfoMatching : function(itemInfo, search) {
+ // Don't show nodisplay items here
+ if (itemInfo.get_is_nodisplay())
+ return false;
// Search takes precedence; not typically useful to search within a
// menu
if (this._activeMenu == null || search != "")
},
_isInfoMatchingMenu : function(itemInfo, search) {
- let id = itemInfo.id;
+ let id = itemInfo.get_id();
for (let i = 0; i < this._activeMenuApps.length; i++) {
- let activeId = this._activeMenuApps[i];
- if (activeId == id)
+ let activeApp = this._activeMenuApps[i];
+ if (activeApp.get_id() == id)
return true;
}
return false;
if (search == null || search == '')
return true;
- let name = itemInfo.name.toLowerCase();
+ let fold = function(s) {
+ return s;
+ if (!s)
+ return s;
+ return GLib.utf8_casefold(GLib.utf8_normalize(s, -1,
+ GLib.NormalizeMode.ALL), -1);
+ };
+ let name = fold(itemInfo.get_name());
if (name.indexOf(search) >= 0)
return true;
- let description = itemInfo.description;
+ let description = fold(itemInfo.get_description());
if (description) {
- description = description.toLowerCase();
if (description.indexOf(search) >= 0)
return true;
}
- if (itemInfo.executable == null) {
+ let exec = fold(itemInfo.get_executable());
+ if (exec == null) {
log("Missing an executable for " + itemInfo.name);
} else {
- let exec = itemInfo.executable.toLowerCase();
if (exec.indexOf(search) >= 0)
return true;
}
- // we expect this._appCategories.hasOwnProperty(itemInfo.id) to always be true here
- let categories = this._appCategories[itemInfo.id];
+ let categories = itemInfo.get_categories();
for (let i = 0; i < categories.length; i++) {
- let category = categories[i].toLowerCase();
+ let category = fold(categories[i]);
if (category.indexOf(search) >= 0)
return true;
}
return false;
},
- // Creates an AppDisplayItem based on itemInfo, which is expected be an AppInfo object.
+ // Creates an AppDisplayItem based on itemInfo, which is expected be an Shell.AppInfo object.
_createDisplayItem: function(itemInfo, width) {
return new AppDisplayItem(itemInfo, width);
}
let iconBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
x_align: Big.BoxAlignment.CENTER });
- this._icon = appInfo.createIcon(APP_ICON_SIZE);
+ this._icon = appInfo.create_icon_texture(APP_ICON_SIZE);
iconBox.append(this._icon, Big.BoxPackFlags.NONE);
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
- this._windows = Shell.AppMonitor.get_default().get_windows_for_app(appInfo.appId)
+ this._windows = Shell.AppMonitor.get_default().get_windows_for_app(appInfo.get_id());
let nameBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
x_align: Big.BoxAlignment.CENTER });
line_alignment: Pango.Alignment.CENTER,
line_wrap: true,
line_wrap_mode: Pango.WrapMode.WORD_CHAR,
- text: appInfo.name });
+ text: appInfo.get_name() });
nameBox.append(this._name, Big.BoxPackFlags.EXPAND);
if (this._windows.length > 0) {
let runningBox = new Big.Box({ /* border_color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
// Draggable interface - FIXME deduplicate with GenericDisplay
getDragActor: function(stageX, stageY) {
- this.dragActor = this.appInfo.createIcon(APP_ICON_SIZE);
+ this.dragActor = this.appInfo.create_icon_texture(APP_ICON_SIZE);
// If the user dragged from the icon itself, then position
// the dragActor over the original icon. Otherwise center it
}));
for (let i = 0; i < infos.length; i++) {
- let display = new WellDisplayItem(infos[i], this.isFavorite);
+ let app = infos[i];
+ let display = new WellDisplayItem(app, this.isFavorite);
display.connect('activated', Lang.bind(this, function (display) {
this.emit('activated', display);
}));
this.actor.add_actor(display.actor);
- };
+ }
},
// Draggable target interface
let id = null;
if (source instanceof WellDisplayItem) {
- id = source.appInfo.appId;
+ id = source.appInfo.get_id();
} else if (source instanceof AppDisplayItem) {
id = source.getId();
} else if (source instanceof Workspaces.WindowClone) {
let appMonitor = Shell.AppMonitor.get_default();
- id = appMonitor.get_window_id(source.metaWindow);
+ let app = appMonitor.get_window_app(source.metaWindow);
+ id = app.get_id();
if (id === null)
return false;
} else {
this._redisplay();
},
+ _lookupApp: function(id) {
+ let app = this._appSystem.lookup_app(id);
+ if (!app) {
+ log("failed to look up app " + id);
+ };
+ return app;
+ },
+
_redisplay: function() {
let arrayToObject = function(a) {
let o = {};
o[a[i]] = 1;
return o;
};
- let favorites = AppInfo.getFavorites();
- let favoriteIds = arrayToObject(favorites.map(function (e) { return e.appId; }));
- let running = AppInfo.getRunning().filter(function (e) {
- return !(e.appId in favoriteIds);
+ let favoriteIds = this._appSystem.get_favorites();
+ let favoriteIdsObject = arrayToObject(favoriteIds);
+ let runningIds = this._appMonitor.get_running_app_ids().filter(function (e) {
+ return !(e in favoriteIdsObject);
});
+ let favorites = favoriteIds.map(Lang.bind(this, this._lookupApp));
+ let running = runningIds.map(Lang.bind(this, this._lookupApp));
this._favoritesArea.redisplay(favorites);
this._runningArea.redisplay(running);
// If it's empty, we hide it so the top border doesn't show up
const Shell = imports.gi.Shell;
const Signals = imports.signals;
-const AppInfo = imports.misc.appInfo;
const DocDisplay = imports.ui.docDisplay;
const DocInfo = imports.misc.docInfo;
}
};
+function AppsWidgetInfo(appInfo) {
+ this._init(appInfo);
+}
+
+AppsWidgetInfo.prototype = {
+ _init : function(appInfo) {
+ this._info = appInfo;
+ },
+
+ createIcon : function(size) {
+ return this._info.create_icon_texture(size);
+ },
+
+ launch : function() {
+ this._info.launch();
+ }
+}
+
function AppsWidget() {
this._init.apply(this, arguments);
}
this.actor = new Big.Box({ spacing: 2 });
this.collapsedActor = new Big.Box({ spacing: 2});
- let apps = AppInfo.getTopApps(5);
- for (let i = 0; i < apps.length; i++)
- this.addItem(apps[i]);
+ let appSystem = Shell.AppSystem.get_default();
+ let apps = appSystem.get_favorites();
+ for (let i = 0; i < apps.length; i++) {
+ let app = appSystem.lookup_app(apps[i]);
+ this.addItem(new AppsWidgetInfo(app));
+ }
}
};
/* <char * appid, guint window_count> */
GHashTable *running_appids;
- /* <MetaWindow * window, char * appid> */
- GHashTable *window_to_appid;
+ /* <MetaWindow * window, ShellAppInfo *app> */
+ GHashTable *window_to_app;
GHashTable *apps_by_wm_class; /* Seen apps by wm_class */
GHashTable *popularities; /* One AppPopularity struct list per activity */
}
/**
- * get_appid_for_window:
+ * get_app_for_window:
*
- * Returns a desktop file ID for an application, or %NULL if
+ * Returns the application associated with a window, or %NULL if
* we're unable to determine one.
*/
-static char *
-get_appid_for_window (MetaWindow *window)
+static ShellAppInfo *
+get_app_for_window (MetaWindow *window)
{
char *wmclass;
char *with_desktop;
- char *result;
+ ShellAppInfo *result;
ShellAppSystem *appsys;
wmclass = get_cleaned_wmclass_for_window (window);
g_free (wmclass);
appsys = shell_app_system_get_default ();
- result = shell_app_system_lookup_basename (appsys, with_desktop);
+ result = shell_app_system_lookup_heuristic_basename (appsys, with_desktop);
g_free (with_desktop);
return result;
track_window (ShellAppMonitor *self,
MetaWindow *window)
{
- char *appid;
+ ShellAppInfo *app;
guint window_count;
+ const char *appid;
- appid = get_appid_for_window (window);
- if (!appid)
+ app = get_app_for_window (window);
+ if (!app)
return;
- g_hash_table_insert (self->window_to_appid, window, appid);
+ /* Steal the ref */
+ g_hash_table_insert (self->window_to_app, window, app);
+
+ appid = shell_app_info_get_id (app);
window_count = GPOINTER_TO_UINT (g_hash_table_lookup (self->running_appids, appid));
gpointer user_data)
{
ShellAppMonitor *self = SHELL_APP_MONITOR (user_data);
- char *appid;
+ ShellAppInfo *app;
+ const char *appid;
guint window_count;
- appid = g_hash_table_lookup (self->window_to_appid, window);
- if (!appid)
+ app = g_hash_table_lookup (self->window_to_app, window);
+ if (!app)
return;
+ appid = shell_app_info_get_id (app);
window_count = GPOINTER_TO_UINT (g_hash_table_lookup (self->running_appids, appid));
window_count -= 1;
g_hash_table_insert (self->running_appids, g_strdup (appid),
GUINT_TO_POINTER (window_count));
}
- g_hash_table_remove (self->window_to_appid, window);
+ g_hash_table_remove (self->window_to_app, window);
}
static void
gpointer key, value;
GSList *ret = NULL;
- g_hash_table_iter_init (&iter, self->window_to_appid);
+ g_hash_table_iter_init (&iter, self->window_to_app);
while (g_hash_table_iter_next (&iter, &key, &value))
{
MetaWindow *window = key;
- const char *id = value;
+ ShellAppInfo *app = value;
+ const char *id = shell_app_info_get_id (app);
if (strcmp (id, appid) != 0)
continue;
self->running_appids = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
- self->window_to_appid = g_hash_table_new_full (g_direct_hash, g_direct_equal,
- NULL, (GDestroyNotify) g_free);
+ self->window_to_app = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) shell_app_info_unref);
load_initial_windows (self);
init_window_monitoring (self);
}
/**
- * shell_app_monitor_get_window_id
+ * shell_app_monitor_get_window_app
* @monitor: An app monitor instance
* @metawin: A #MetaWindow
*
- * Returns: (transfer full): Desktop file id associated with window
+ * Returns: Desktop file id associated with window
*/
-char *
-shell_app_monitor_get_window_id (ShellAppMonitor *monitor, MetaWindow *metawin)
+ShellAppInfo *
+shell_app_monitor_get_window_app (ShellAppMonitor *monitor, MetaWindow *metawin)
{
- return g_hash_table_lookup (monitor->window_to_appid, metawin);
+ ShellAppInfo *info = g_hash_table_lookup (monitor->window_to_app, metawin);
+ if (info)
+ shell_app_info_ref (info);
+ return info;
}
/**
#include <string.h>
#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+#include <gtk/gtk.h>
#include <gconf/gconf.h>
#include <gconf/gconf-client.h>
+#include "shell-global.h"
+#include "shell-texture-cache.h"
+#include "display.h"
+
#define GMENU_I_KNOW_THIS_IS_UNSTABLE
#include <gmenu-tree.h>
GMenuTree *apps_tree;
GMenuTree *settings_tree;
+ GHashTable *app_id_to_app;
+
GSList *cached_app_menus; /* ShellAppMenuEntry */
- GSList *cached_setting_ids; /* utf8 */
+ GSList *cached_settings; /* ShellAppInfo */
GHashTable *cached_favorites; /* <utf8,integer> */
G_DEFINE_TYPE(ShellAppSystem, shell_app_system, G_TYPE_OBJECT);
+ShellAppInfo*
+shell_app_info_ref (ShellAppInfo *info)
+{
+ return gmenu_tree_item_ref ((GMenuTreeItem*)info);
+}
+
+void
+shell_app_info_unref (ShellAppInfo *info)
+{
+ gmenu_tree_item_unref ((GMenuTreeItem *)info);
+}
+
static gpointer
shell_app_menu_entry_copy (gpointer entryp)
{
(GDestroyNotify)g_free,
NULL);
+ /* The key is owned by the value */
+ priv->app_id_to_app = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify) shell_app_info_unref);
+
/* For now, we want to pick up Evince, Nautilus, etc. We'll
* handle NODISPLAY semantics at a higher level or investigate them
* case by case.
gmenu_tree_unref (priv->apps_tree);
gmenu_tree_unref (priv->settings_tree);
+ g_hash_table_destroy (priv->app_id_to_app);
+
g_slist_foreach (priv->cached_app_menus, (GFunc)shell_app_menu_entry_free, NULL);
g_slist_free (priv->cached_app_menus);
priv->cached_app_menus = NULL;
- g_slist_foreach (priv->cached_setting_ids, (GFunc)g_free, NULL);
- g_slist_free (priv->cached_setting_ids);
- priv->cached_setting_ids = NULL;
+ g_slist_foreach (priv->cached_settings, (GFunc)shell_app_info_unref, NULL);
+ g_slist_free (priv->cached_settings);
+ priv->cached_settings = NULL;
g_hash_table_destroy (priv->cached_favorites);
static GSList *
gather_entries_recurse (ShellAppSystem *monitor,
- GSList *ids,
+ GSList *apps,
GMenuTreeDirectory *root)
{
GSList *contents;
{
case GMENU_TREE_ITEM_ENTRY:
{
- GMenuTreeEntry *entry = (GMenuTreeEntry *)item;
- const char *id = gmenu_tree_entry_get_desktop_file_id (entry);
- if (!gmenu_tree_entry_get_is_nodisplay (entry))
- ids = g_slist_prepend (ids, g_strdup (id));
+ apps = g_slist_prepend (apps, item);
}
break;
case GMENU_TREE_ITEM_DIRECTORY:
{
GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
- ids = gather_entries_recurse (monitor, ids, dir);
+ apps = gather_entries_recurse (monitor, apps, dir);
}
+ gmenu_tree_item_unref (item);
break;
default:
+ gmenu_tree_item_unref (item);
break;
}
- gmenu_tree_item_unref (item);
}
g_slist_free (contents);
- return ids;
+ return apps;
}
static void
trunk = gmenu_tree_get_root_directory (tree);
- g_slist_foreach (*cache, (GFunc)g_free, NULL);
+ g_slist_foreach (*cache, (GFunc)shell_app_info_unref, NULL);
g_slist_free (*cache);
*cache = NULL;
gmenu_tree_item_unref (trunk);
}
+static void
+cache_by_id (ShellAppSystem *self, GSList *apps, gboolean ref)
+{
+ GSList *iter;
+
+ for (iter = apps; iter; iter = iter->next)
+ {
+ ShellAppInfo *info = iter->data;
+ if (ref)
+ gmenu_tree_item_ref ((GMenuTreeItem*) info);
+ /* the name is owned by the info itself */
+ g_hash_table_insert (self->priv->app_id_to_app, (char*)shell_app_info_get_id (info),
+ info);
+ }
+}
+
static void
reread_menus (ShellAppSystem *self)
{
+ GSList *apps;
+ GMenuTreeDirectory *trunk;
+
reread_directories (self, &(self->priv->cached_app_menus), self->priv->apps_tree);
- reread_entries (self, &(self->priv->cached_setting_ids), self->priv->settings_tree);
+
+ reread_entries (self, &(self->priv->cached_settings), self->priv->settings_tree);
+
+ /* Now loop over applications.menu and settings.menu, inserting each by desktop file
+ * ID into a hash */
+ g_hash_table_remove_all (self->priv->app_id_to_app);
+ trunk = gmenu_tree_get_root_directory (self->priv->apps_tree);
+ apps = gather_entries_recurse (self, NULL, trunk);
+ gmenu_tree_item_unref (trunk);
+ cache_by_id (self, apps, FALSE);
+ g_slist_free (apps);
+ cache_by_id (self, self->priv->cached_settings, TRUE);
}
static void
g_signal_emit (G_OBJECT (system), signals[FAVORITES_CHANGED], 0);
}
+GType
+shell_app_info_get_type (void)
+{
+ static GType gtype = G_TYPE_INVALID;
+ if (gtype == G_TYPE_INVALID)
+ {
+ gtype = g_boxed_type_register_static ("ShellAppInfo",
+ (GBoxedCopyFunc)shell_app_info_ref,
+ (GBoxedFreeFunc)shell_app_info_unref);
+ }
+ return gtype;
+}
+
GType
shell_app_menu_entry_get_type (void)
{
* Traverses a toplevel menu, and returns all items under it. Nested items
* are flattened.
*
- * Return value: (transfer full) (element-type utf8): List of desktop file ids
+ * Return value: (transfer container) (element-type ShellAppInfo): List of applications
*/
GSList *
shell_app_system_get_applications_for_menu (ShellAppSystem *monitor,
/**
* shell_app_system_get_menus:
*
- * Returns a list of toplevel menu names, like "Accessories", "Programming", etc.
+ * Returns a list of toplevel #ShellAppMenuEntry items
*
* Return value: (transfer none) (element-type AppMenuEntry): List of toplevel menus
*/
/**
* shell_app_system_get_all_settings:
*
- * Returns a list of all desktop file ids under "settings.menu".
+ * Returns a list of application items under "settings.menu".
*
- * Return value: (transfer none) (element-type utf8): List of desktop file ids
+ * Return value: (transfer none) (element-type ShellAppInfo): List of applications
*/
GSList *
shell_app_system_get_all_settings (ShellAppSystem *monitor)
{
- return monitor->priv->cached_setting_ids;
+ return monitor->priv->cached_settings;
}
/**
gconf_client_set (client, SHELL_APP_FAVORITES_KEY, val, NULL);
}
-static gboolean
-desktop_id_exists (ShellAppSystem *system,
- const char *target_id,
- GMenuTreeDirectory *root)
+/**
+ * shell_app_system_lookup_app:
+ *
+ * Return value: (transfer full): The #ShellAppInfo for id, or %NULL if none
+ */
+ShellAppInfo *
+shell_app_system_lookup_app (ShellAppSystem *self, const char *id)
{
- gboolean found = FALSE;
- GSList *contents, *iter;
+ GMenuTreeEntry *entry;
- contents = gmenu_tree_directory_get_contents (root);
+ entry = g_hash_table_lookup (self->priv->app_id_to_app, id);
+ if (entry)
+ gmenu_tree_item_ref ((GMenuTreeItem*) entry);
+ return (ShellAppInfo*)entry;
+}
- for (iter = contents; iter; iter = iter->next)
- {
- GMenuTreeItem *item = iter->data;
+/**
+ * shell_app_system_lookup_heuristic_basename:
+ * @name: Probable application identifier
+ *
+ * Find a valid application corresponding to a given
+ * heuristically determined application identifier
+ * string, or %NULL if none.
+ *
+ * Returns: (transfer full): A #ShellAppInfo for name
+ */
+ShellAppInfo *
+shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
+ const char *name)
+{
+ char *tmpid;
+ ShellAppInfo *result;
- if (found)
- break;
+ result = shell_app_system_lookup_app (system, name);
+ if (result != NULL)
+ return result;
- switch (gmenu_tree_item_get_type (item))
+ /* These are common "vendor prefixes". But using
+ * WM_CLASS as a source, we don't get the vendor
+ * prefix. So try stripping them.
+ */
+ tmpid = g_strjoin ("", "gnome-", name, NULL);
+ result = shell_app_system_lookup_app (system, tmpid);
+ g_free (tmpid);
+ if (result != NULL)
+ return result;
+
+ tmpid = g_strjoin ("", "fedora-", name, NULL);
+ result = shell_app_system_lookup_app (system, tmpid);
+ g_free (tmpid);
+ if (result != NULL)
+ return result;
+
+ return NULL;
+}
+
+const char *
+shell_app_info_get_id (ShellAppInfo *info)
+{
+ return gmenu_tree_entry_get_desktop_file_id ((GMenuTreeEntry*)info);
+}
+
+const char *
+shell_app_info_get_name (ShellAppInfo *info)
+{
+ return gmenu_tree_entry_get_name ((GMenuTreeEntry*)info);
+}
+
+const char *
+shell_app_info_get_description (ShellAppInfo *info)
+{
+ return gmenu_tree_entry_get_comment ((GMenuTreeEntry*)info);
+}
+
+const char *
+shell_app_info_get_executable (ShellAppInfo *info)
+{
+ return gmenu_tree_entry_get_exec ((GMenuTreeEntry*)info);
+}
+
+GIcon *
+shell_app_info_get_icon (ShellAppInfo *info)
+{
+ const char *iconname;
+ GIcon *icon;
+
+ /* This code adapted from gdesktopappinfo.c
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright © 2007 Ryan Lortie
+ * LGPL
+ */
+
+ iconname = gmenu_tree_entry_get_icon ((GMenuTreeEntry*)info);
+ if (!iconname)
+ return NULL;
+
+ if (g_path_is_absolute (iconname))
+ {
+ GFile *file;
+
+ file = g_file_new_for_path (iconname);
+ icon = G_ICON (g_file_icon_new (file));
+ g_object_unref (file);
+ }
+ else
+ {
+ char *tmp_name, *p;
+ tmp_name = strdup (iconname);
+ /* Work around a common mistake in desktop files */
+ if ((p = strrchr (tmp_name, '.')) != NULL &&
+ (strcmp (p, ".png") == 0 ||
+ strcmp (p, ".xpm") == 0 ||
+ strcmp (p, ".svg") == 0))
{
- case GMENU_TREE_ITEM_ENTRY:
- {
- GMenuTreeEntry *entry = (GMenuTreeEntry *)item;
- const char *id = gmenu_tree_entry_get_desktop_file_id (entry);
- if (strcmp (id, target_id) == 0)
- found = TRUE;
- }
- break;
- case GMENU_TREE_ITEM_DIRECTORY:
- {
- GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
- found = desktop_id_exists (system, target_id, dir);
- }
- break;
- default:
- break;
+ *p = 0;
}
- gmenu_tree_item_unref (item);
+
+ icon = g_themed_icon_new (tmp_name);
+ g_free (tmp_name);
}
+ return icon;
+}
- g_slist_free (contents);
+GSList *
+shell_app_info_get_categories (ShellAppInfo *info)
+{
+ return NULL; /* TODO */
+}
- return found;
+gboolean
+shell_app_info_get_is_nodisplay (ShellAppInfo *info)
+{
+ return gmenu_tree_entry_get_is_nodisplay ((GMenuTreeEntry*)info);
}
/**
- * shell_app_system_lookup_basename:
- * @name: Probable application identifier
+ * shell_app_info_create_icon_texture:
*
- * Determine whether a valid .desktop file ID corresponding to a given
- * heuristically determined application identifier
- * string.
+ * Look up the icon for this application, and create a #ClutterTexture
+ * for it at the given size.
+ *
+ * Return value: (transfer none): A floating #ClutterActor
+ */
+ClutterActor *
+shell_app_info_create_icon_texture (ShellAppInfo *info, float size)
+{
+ GIcon *icon;
+ ClutterActor *ret;
+
+ icon = shell_app_info_get_icon (info);
+ if (!icon)
+ {
+ ret = clutter_texture_new ();
+ g_object_set (ret, "width", size, "height", size, NULL);
+ return ret;
+ }
+
+ return shell_texture_cache_load_gicon (shell_texture_cache_get_default (), icon, (int)size);
+}
+
+/**
+ * shell_app_info_launch_full:
+ * @timestamp: Event timestamp, or 0 for current event timestamp
+ * @uris: List of uris to pass to application
+ * @workspace: Start on this workspace, or -1 for default
+ * @startup_id: (out): Returned startup notification ID, or %NULL if none
+ * @error: A #GError
*/
-char *
-shell_app_system_lookup_basename (ShellAppSystem *system,
- const char *name)
+gboolean
+shell_app_info_launch_full (ShellAppInfo *info,
+ guint timestamp,
+ GList *uris,
+ int workspace,
+ char **startup_id,
+ GError **error)
{
- GMenuTreeDirectory *root;
- char *result;
+ GDesktopAppInfo *gapp;
+ const char *filename;
+ GdkAppLaunchContext *context;
+ gboolean ret;
+ ShellGlobal *global;
+ MetaScreen *screen;
+ MetaDisplay *display;
- root = gmenu_tree_get_directory_from_path (system->priv->apps_tree, "/");
- g_assert (root != NULL);
+ if (startup_id)
+ *startup_id = NULL;
- if (desktop_id_exists (system, name, root))
+ filename = gmenu_tree_entry_get_desktop_file_path ((GMenuTreeEntry*) info);
+
+ gapp = g_desktop_app_info_new_from_filename (filename);
+ if (!gapp)
{
- result = g_strdup (name);
- goto out;
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Not found");
+ return FALSE;
}
- /* These are common "vendor prefixes". But using
- * WM_CLASS as a source, we don't get the vendor
- * prefix. So try stripping them.
- */
- result = g_strjoin ("", "gnome-", name, NULL);
- if (desktop_id_exists (system, result, root))
- goto out;
+ global = shell_global_get ();
+ screen = shell_global_get_screen (global);
+ display = meta_screen_get_display (screen);
+
+ if (timestamp == 0)
+ timestamp = meta_display_get_current_time (display);
+ if (workspace < 0)
+ workspace = meta_screen_get_active_workspace_index (screen);
+
+ context = gdk_app_launch_context_new ();
+ gdk_app_launch_context_set_timestamp (context, timestamp);
+ gdk_app_launch_context_set_desktop (context, workspace);
+
+ ret = g_app_info_launch (G_APP_INFO (gapp), uris, (GAppLaunchContext*) context, error);
- result = g_strjoin ("", "fedora-", name, NULL);
- if (desktop_id_exists (system, result, root))
- goto out;
+ g_object_unref (G_OBJECT (gapp));
-out:
- gmenu_tree_item_unref (root);
- return result;
+ return ret;
+}
+
+gboolean
+shell_app_info_launch (ShellAppInfo *info,
+ GError **error)
+{
+ return shell_app_info_launch_full (info, 0, NULL, -1, NULL, error);
}
#ifndef __SHELL_APP_SYSTEM_H__
#define __SHELL_APP_SYSTEM_H__
-#include <glib-object.h>
+#include <gio/gio.h>
+#include <clutter/clutter.h>
#define SHELL_TYPE_APP_SYSTEM (shell_app_system_get_type ())
#define SHELL_APP_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_APP_SYSTEM, ShellAppSystem))
GType shell_app_menu_entry_get_type (void);
-char * shell_app_system_lookup_basename (ShellAppSystem *system, const char *id);
+/* Hidden typedef for a GMenuTreeEntry */
+typedef struct _ShellAppInfo ShellAppInfo;
+
+GType shell_app_info_get_type (void);
+
+ShellAppInfo* shell_app_info_ref (ShellAppInfo *info);
+void shell_app_info_unref (ShellAppInfo *info);
+
+const char *shell_app_info_get_id (ShellAppInfo *info);
+const char *shell_app_info_get_name (ShellAppInfo *info);
+const char *shell_app_info_get_description (ShellAppInfo *info);
+const char *shell_app_info_get_executable (ShellAppInfo *info);
+GIcon *shell_app_info_get_icon (ShellAppInfo *info);
+ClutterActor *shell_app_info_create_icon_texture (ShellAppInfo *info, float size);
+GSList *shell_app_info_get_categories (ShellAppInfo *info);
+gboolean shell_app_info_get_is_nodisplay (ShellAppInfo *info);
+gboolean shell_app_info_launch_full (ShellAppInfo *info,
+ guint timestamp,
+ GList *uris,
+ int workspace,
+ char **startup_id,
+ GError **error);
+gboolean shell_app_info_launch (ShellAppInfo *info,
+ GError **error);
+
+ShellAppInfo *shell_app_system_lookup_app (ShellAppSystem *system, const char *id);
+
+ShellAppInfo *shell_app_system_lookup_heuristic_basename (ShellAppSystem *system, const char *id);
GSList *shell_app_system_get_menus (ShellAppSystem *system);
return pixbuf;
}
-
-/**
- * shell_get_categories_for_desktop_file:
- *
- * @desktop_file_name: name of the desktop file for which to retrieve categories
- *
- * Return value: (element-type char*) (transfer full): List of categories
- *
- */
-GSList *
-shell_get_categories_for_desktop_file(const char *desktop_file_name)
-{
- GKeyFile *key_file;
- const char * const *search_dirs;
- char **categories = NULL;
- GSList *categories_list = NULL;
- GError *error = NULL;
- gsize len;
- int i;
-
- key_file = g_key_file_new ();
- search_dirs = get_applications_search_path();
-
- g_key_file_load_from_dirs (key_file, desktop_file_name, (const char **)search_dirs, NULL, 0, &error);
-
- if (error != NULL)
- {
- g_warning ("Error when loading a key file for %s: %s", desktop_file_name, error->message);
- g_clear_error (&error);
- }
- else
- {
- categories = g_key_file_get_string_list (key_file,
- "Desktop Entry",
- "Categories",
- &len,
- &error);
- if (error != NULL)
- {
- // "Categories" is not a required key in the desktop files, so it's ok if we didn't find it
- g_clear_error (&error);
- }
- }
-
- g_key_file_free (key_file);
-
- if (categories == NULL)
- return NULL;
-
- // gjs currently does not support returning arrays (other than a NULL value for an array), so we need
- // to convert the array we are returning to GSList, returning which gjs supports.
- // See http://bugzilla.gnome.org/show_bug.cgi?id=560567 for more info on gjs array support.
- for (i = 0; categories[i]; i++)
- {
- categories_list = g_slist_prepend (categories_list, g_strdup (categories[i]));
- }
-
- g_strfreev (categories);
-
- return categories_list;
-}
-
/**
* shell_get_event_key_symbol:
*
GdkPixbuf *shell_get_thumbnail(const gchar *uri, const gchar *mime_type);
-GSList *shell_get_categories_for_desktop_file(const char *desktop_file_name);
-
guint16 shell_get_event_key_symbol(ClutterEvent *event);
guint16 shell_get_button_event_click_count(ClutterEvent *event);